﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Mindless
{
    public class ChoiceTree
    {
        /// <summary>
        /// Creates a tree of possible play-outs for the given deal.
        /// </summary>
        public ChoiceTree(Deal deal, Game game)
        {
            Debug.Assert(deal != null);
            Debug.Assert(game != null);

            this.deal = deal;
            this.game = game;
        }

        /// <summary>
        /// The deal being walked.
        /// </summary>
        protected Deal deal;

        /// <summary>
        /// Each player's current score in the deal being walked.
        /// </summary>
        protected Game game;

        /// <summary>
        /// Determines the value of the given choice for current tree node.
        /// </summary>
        /// <returns>The values of this choice from the perspective of each team.</returns>
        public int[] Evaluate(IChoice choice, int lookahead, bool forceAssignGamePoint, List<IChoice> path)
        {
            using (new DealCookieHolder(this.deal, choice))
            {
                int[] result = null;

                    // leaf node
                if (((this.deal.ActivePlayer == 0) && (lookahead <= 0)) || this.deal.IsOver)
                {
                        // determine the state of the deal at this node
                    Deal.BidStateEnum bidState = this.deal.BidState(forceAssignGamePoint, out result);
                    if (bidState == Deal.BidStateEnum.Set)
                        this.deal.ApplySetback(result);

                        // if this ends the game, adjust values accordingly
                    if (!forceAssignGamePoint)
                    {
                        this.game.Push(result);
                        if (this.game.IsOver)
                        {
                            for (int iTeam = 0; iTeam < Settings.TeamCount; ++iTeam)
                                result[iTeam] = (iTeam == this.game.WinningTeam) ? 5 : -5;
                        }
                        this.game.Pop();
                    }
                }
                    // non-leaf node
                else
                {
                    int bestNetValue = int.MinValue;
                    int pathDepth = path == null ? -1 : path.Count;
                    foreach (IChoice next in this.deal.GetChoices())
                    {
                        List<IChoice> subpath = null;
                        if (path != null)
                        {
                            subpath = new List<IChoice>();
                            subpath.Add(next);
                        }

                        int[] values = this.Evaluate(next, lookahead - 1, forceAssignGamePoint, subpath);
                        int netValue = deal.NetValue(values);
                        if (netValue >= bestNetValue)   // style: prefer the larger play
                        {
                            bestNetValue = netValue;
                            result = values;

                            if (path != null)
                            {
                                path.RemoveRange(pathDepth, path.Count - pathDepth);
                                path.AddRange(subpath);
                            }
                        }
                    }
                }

                return result;
            }
        }
    }
}
